<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ios-remote</title>
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <!-- 可选的 Bootstrap 主题文件（一般不用引入） -->
    <link rel="stylesheet" href="/css/bootstrap-theme.min.css"
          integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
</head>
<script src="//cdn.jsdelivr.net/g/vue@2.1.10,jquery@3.1.1"></script>
<script src="/js/vue.js"></script>
<script src="/js/canvas2image.js"></script>
<script src="/js/upload.js"></script>
<script src="/js/ajaxfileupload.js"></script>
<script src="/js/bootstrap.min.js"></script>
<body>

<!--<canvas id="screen" style="border: 1px solid #ac7fff;"></canvas>-->

<div id="app" class="container height100p">
    <div class="row height100p">
        <div class="col-sm-6 height100p">
            <div class="screen-wraper">
                <div style="text-align:center" class="form-group">
                    <span class="finger finger-0" style="transform: translate3d(0, 0, 0)"></span>
                    <span class="finger finger-1"></span>
                    <canvas id="screen" style="border: 1px solid #ac7fff;"></canvas>
                    <div style="text-align:center" class="form-group">
                        <button class="btn btn-default" onclick="home()">
                            <span class="glyphicon glyphicon-home"></span> Home
                        </button>
                    </div>
                </div>
            </div>
            <div>
                <p>
                    <button id="save">保存截图</button>
                    格式:
                    <select id="sel">
                        <option value="png">png</option>
                        <option value="jpeg">jpeg</option>
                        <option value="bmp">bmp</option>
                    </select><br/>
                    (可选)截图文件名: <input type="text" placeholder="optional" id="imgFileName">
                </p>

            </div>
        </div>

        <div class="col-sm-6 ">
            打开页面之后需要约20秒时间启动后台WDA程序之后才能显示手机页面进行使用，
            <br/>
            请耐心等待，不要使劲刷新...
        </div>
        <div class="col-sm-6">
            <hr>

            <div class="form-group">要使用sentText/clear功能，请在下方输入bundleId并启动App，无法夸app使用sendText.</div>
            <div class="form-group">
                <div class="input-group">
                    <input id="bundleid" type="text" class="form-control" v-model="inputText"
                           placeholder="请输入bundle id">
                    <span class="input-group-btn">
              <button class="btn btn-default" type="button" onclick="launchApp()">以BundleId启动App</button>
                        </span>
                </div>
            </div>
            <div class="form-group">
                <div class="input-group">
                    <input id="input" type="text" class="form-control" v-model="inputText"
                           placeholder="Input text ..."
                           @keyup.enter="sendText(inputText+'\n')">
                    <span class="input-group-btn">
              <button class="btn btn-default" type="button" onclick="sendText()">发送文本</button>
              <button class="btn btn-default" type="button" onclick="clearText()">清空</button>
            </span>
                </div>
            </div>

            <div class="form-group">
                <div class="input-group">

                    <input type="file" name="uploadfile" id="uploadfile">
                    <button id="upload" type="button" onclick="return false;">上传并安装ipa</button>

                </div>
            </div>
            <div class="form-group">
                <input id="uninstallid" type="text" class="form-control" v-model="inputText"
                       placeholder="请输入bundle id">
                <span class="input-group-btn">
              <button class="btn btn-default" type="button" onclick="uninstallApp()">以BundleId卸载App</button>
                        </span>
            </div>

            <div class="panel-heading">
                <h3 class="panel-title">设备详情</h3>
            </div>
            <div class="btn btn-default" onclick="refreshdetail()">刷新</div>

            <table id="deviceinfo" class="table">
                <div id="getlogbtn" class="btn btn-default" onclick="refreshlogs()">开始</div>
                <tbody>
                <tr>
                    <td>设备名称：</td>
                    <td id="name" v-text="device.name"></td>
                </tr>
                <tr>
                    <td>UDID：</td>
                    <td id="udid" v-text="device.udid"></td>
                </tr>
                <tr>
                    <td>已安装的App：</td>
                    <td id="apps" v-html="device.apps"></td>
                </tr>
                <tr>

                    <td>日志：</td>
                    <td id="logs" v-html="device.logs"></td>
                </tr>
                </tbody>

            </table>


        </div>


    </div>

</div>

</body>

<script>

    var canvas, ctx,
        $save,
        $imgW, $imgH,
        $sel;

    function init() {
        canvas = document.getElementById('screen');
        ctx = canvas.getContext('2d');
        $save = document.getElementById('save');
        $sel = document.getElementById('sel');
        $imgW = document.getElementById('imgW');
        $imgH = document.getElementById('imgH');
        $imgFileName = document.getElementById('imgFileName');
        bind();
    }

    function bind() {
        $save.onclick = function (e) {
            var type = $sel.value,
                w = 375,
                h = 667;
            f = $imgFileName.value;
            Canvas2Image.saveAsImage(canvas, w, h, type, f);
        }

    }

    onload = init;
</script>
<script>
    var WSURL2 = "ws://" + window.location.host + "/websocket2"
    var ws2 = null;

    var deviceDetail = new Vue({
        el: "#deviceinfo",
        data: {
            device: {
                name: '',
                udid: '',
                apps: '',
                logs: '',
            },
        },
        mounted: function () {
            this.refresh();

        },
        methods: {
            refresh: function () {
                console.log("getting device info...");
                var self = this;
                $.ajax({
                    url: "/ios/getDeviceInfo?",
                    method: "GET",
                })
                    .then(function (ret) {
                        self.device.udid = ret.udid
                        self.device.name = ret.name
                        self.device.apps = ret.apps
                        console.log(udid)
                    })
            },

        },
    });


    function refreshdetail() {
        console.log("getting device info...");
        $.ajax({
            url: "/ios/getDeviceInfo?",
            method: "GET",
        })
            .then(function (ret) {
                deviceDetail.device.udid = ret.udid
                deviceDetail.device.name = ret.name
                deviceDetail.device.apps = ret.apps
                console.log(udid)
            })
    }

    function refreshlogs() {

        if (document.getElementById('getlogbtn').textContent == "开始") {
            deviceDetail.device.logs = "";
            console.log("开始抓取日志...");
            ws2 = new WebSocket(WSURL2)

            ws2.onclose = function () {
                console.log('ws2 onclose', arguments)
            }

            ws2.onerror = function () {
                console.log('ws2 onerror', arguments)
            }

            ws2.onmessage = function (message) {
                deviceDetail.device.logs += message.data
                console.log('msg got:' + message.data)
            }
            ws2.onopen = function () {
                console.log('ws2 onopen', arguments)

            }
            document.getElementById('getlogbtn').textContent = "停止"
            deviceDetail.device.getlog = "停止"
        } else {
            ws2.close();
            console.log("停止抓取日志...");
            document.getElementById('getlogbtn').textContent = "开始"
        }


    }

    function home() {
        $.ajax({
            url: "/ios/home",
            method: "GET",
        })
            .then(function (ret) {
                console.log(ret);
            })
    }

    function launchApp() {
        var id = document.getElementById('bundleid').value
        if (id != "") {
            $.ajax({
                url: "/ios/launchapp?id=" + id,
                method: "GET",

            })
                .then(function (ret) {
                    console.log(ret);
                })
        }
    }

    function uninstallApp() {
        var id = document.getElementById('uninstallid').value
        if (id != "") {
            $.ajax({
                url: "/ios/uninstall?id=" + id,
                method: "GET",

            })
                .then(function (ret) {
                    console.log(ret);
                })
        }
    }

    function getDeviceInfo() {
        var udid = "";
        $.ajax({
            url: "/ios/getDeviceInfo?",
            method: "GET",
        })
            .then(function (ret) {
                udid = ret.udid
                console.log(udid)
            })
    }

    function sendText() {
        var sendValue = encodeURI(encodeURI(document.getElementById('input').value))
        console.log("Input Text = " + sendValue)
        $.ajax({
            url: "/ios/inputText?value=" + sendValue,
            method: "GET",

        })
            .then(function (ret) {
                console.log(ret);
            })
    }

    function clearText() {
        $.ajax({
            url: "/ios/clear",
            method: "GET",
        })
            .then(function (ret) {
                console.log(ret);
            })
    }

</script>


<script>
    var app = new Vue({
        el: "#screen",
        // delimiters: ["[[", "]]"],
        data: {
            display: {
                width: 375,
                height: 667,
                naturalWidth: 0,
                naturalHeight: 0,
            },
            refreshCount: 0,
            sessionId: null,
            pageHidden: false,
            current: 0,
            inputText: '',
            wsAdmin: null,
            record: {
                running: false,
                startTime: 0,
                ticker: null,
                duration: 0,
                prompt: '',
            },
            records: [{
                name: "20170603-test",
            }]
        },


        filters: {
            formatDuration: function (value) {
                function pad2(number) {
                    return (number < 10 ? '0' : '') + number
                }

                var totalSeconds = Math.floor(value / 1000); //"00:00:00"
                var mms = value % 1000;
                var ms = totalSeconds % 60;
                var mm = (totalSeconds - ms) / 60
                return "" + pad2(mm) + " : " + pad2(ms);
            }
        },
        mounted: function () {
            var self = this;

            this.initTouchListener();
            this.initScreenSize();
            var self = this;
            $("#screen")[0].onload = function (e) {
                var nw = e.target.naturalWidth;
                var nh = e.target.naturalHeight;
                if (nw && nh && self.display.naturalHeight != nh) {
                    self.display.naturalWidth = nw;
                    self.display.naturalHeight = nh;
                    self.initScreenSize();
                    console.log("Detect screen rotate");
                }
            }.bind(this);
        },
        methods: {

            tap: function (x, y) {
                var self = this;
                return $.ajax({
                    url: "/ios/tap/?x=" + x + "&y=" + y,
                    method: "GET",
                }).then(function (ret) {
                    if (ret.status !== 0) {
                        console.log(ret.value);
                        return "Fail"
                    } else {
                        return "Success";
                    }
                })
            },
            tapHold: function (x, y, duration) {
                var self = this;
                duration = duration / 1000
                return $.ajax({
                    url: "/ios/tapHold?x=" + x + "&y=" + y + "&duration=" + duration,
                    method: "GET",
                }).then(function (ret) {
                    if (ret.status !== 0) {
                        console.log(ret.value);
                    } else {
                        return "Success";
                    }
                })
            },
            swipe: function (fromX, fromY, toX, toY) {
                var self = this;
                return $.ajax({
                    url: "/ios/swipe?fromX=" + fromX + "&fromY=" + fromY + "&toX=" + toX + "&toY=" + toY + "&duration=" + 0.2,
                    method: "GET",
                })
            },
            home: function () {
                return $.ajax({
                    url: "/ios/homeScreen",
                    method: "GET",
                })
                    .then(function (ret) {
                        console.log(ret);
                    })
            },
            sendText: function (text) {
                inputText = document.getElementById('input').value
                return $.ajax({
                    url: "/ios/inputText?value=" + inputText,
                    method: "GET",

                })
                    .then(function (ret) {
                        console.log(ret);
                    })
            },

            clearText: function (n) {
                this.sendText("\b".repeat(n || 30))
            },

            initScreenSize: function () {
//                $.ajax({
//                    url: "/ios/status",
//                })
//                    .then(function(ret) {
//                        this.sessionId = ret.sessionId;
//                        return $.ajax({
//                            url: "/ios/windowsize",
//                        })
//                    }.bind(this))
//                    .then(function(ret) {
//                        console.log("ret value : " + ret.value);
//                        this.display.width = ret.value.width;
//                        this.display.height = ret.value.height;
//                    }.bind(this))
            },
            initTouchListener: function () {
                var self = this;
                var element = $("#screen")[0];
                var screen = {
                    bounds: {},
                    firstPosition: {},
                    secondPosition: {},
                    beginTime: null,
                }
                var _longClickTimeout = 800;

                function calculateBounds() {
                    var el = element;
                    screen.bounds.w = el.offsetWidth
                    screen.bounds.h = el.offsetHeight
                    screen.bounds.x = 0
                    screen.bounds.y = 0

                    while (el.offsetParent) {
                        screen.bounds.x += el.offsetLeft
                        screen.bounds.y += el.offsetTop
                        el = el.offsetParent
                    }
                }

                function deactiveFinger(index) {
                    $(".finger-" + index).removeClass("active").removeClass("longClick")
                }

                function convertPosition(event) {
                    var e = event;
                    if (e.originalEvent) {
                        e = e.originalEvent
                    }
                    // Ignore mouse right click
                    if (e.which === 3) {
                        return;
                    }
                    e.preventDefault()
                    calculateBounds()
                    console.log("page x: " + e.pageX + ", page y: " + e.pageY)
                    var x = e.pageX - screen.bounds.x - 30
                    var y = e.pageY - screen.bounds.y

                    return {
                        e: e,
                        x: Math.floor(x / (element.clientWidth - 60) * self.display.width),
                        y: Math.floor(y / element.clientHeight * self.display.height),
                        pageX: x,
                        pageY: y,
                    }
                }

                function stopMousing() {
                    element.removeEventListener('mousemove', mouseMoveListener);
                    document.removeEventListener('mouseup', mouseUpListener);
                }

                function mouseUpListener(event) {
                    var p = convertPosition(event);
                    stopMousing()

                    var first = screen.firstPosition,
                        second = screen.secondPosition;

                    screen.secondPosition = {};
                    element.style.cursor = 'not-allowed'
                    clearTimeout(screen.longClickKey);
                    var defer;
                    if (second && (second.x || second.y)) {
                        console.log("Swipe", first, second);
                        defer = self.swipe(first.x, first.y, second.x, second.y);
                    } else {
                        var holdTime = new Date().getTime() - screen.beginTime;
                        if (holdTime > _longClickTimeout) {
                            console.log("LongTap", first);
                            defer = self.tapHold(first.x, first.y, holdTime)
                        } else {
                            console.log("Tap", first);
                            defer = self.tap(first.x, first.y)
                        }
                    }
                    defer.then(function () {
                        console.log("Release operation")
                        deactiveFinger(0);
                        deactiveFinger(1);
                        element.style.cursor = 'auto'
                    })
                }

                function mouseMoveListener(event) {
                    var p = convertPosition(event);
                    var first = screen.firstPosition;
                    var distance = Math.sqrt((first.x - p.x) * (first.x - p.x) + (first.y - p.y) * (first.y - p.y));
                    if (distance > 10) {
                        screen.secondPosition = {
                            x: p.x,
                            y: p.y
                        };
                        console.log(p);
                        $(".finger-1")
                            .addClass("active")
                            .css("transform", 'translate3d(' + p.pageX + 'px,' + p.pageY + 'px,0)')
                    } else {
                        deactiveFinger("1");
                        screen.secondPosition = {};
                    }
                }

                function mouseDownListener(event) {
                    if (element.style.cursor == 'not-allowed') {
                        return;
                    }
                    var p = convertPosition(event);
                    var index = 0;
                    $(".finger-" + index)
                        .addClass("active")
                        .css("transform", 'translate3d(' + p.pageX + 'px,' + p.pageY + 'px,0)')

                    screen.longClickKey = setTimeout(function () {
                        $(".finger-0").addClass("longClick");
                    }, _longClickTimeout)

                    screen.beginTime = new Date().getTime();
                    screen.firstPosition = {
                        x: p.x,
                        y: p.y
                    }

                    element.addEventListener('mousemove', mouseMoveListener);
                    document.addEventListener('mouseup', mouseUpListener);
                }

                element.addEventListener("mousedown", mouseDownListener);
            }
        }
    })
</script>


<script>
    var WSURL = "ws://" + window.location.host + "/websocket"
    /*jshint browser:true*/

    // BackFrame stores the latest frame retrieved from the socket. It does no
    // other work and can be swapped at any time.
    function BackFrame() {
        this.blob = null
    }

    BackFrame.prototype.swap = function (blob) {
        this.blob = blob
    }

    BackFrame.prototype.consume = function () {
        var blob = this.blob
        this.blob = null
        return blob
    }

    BackFrame.prototype.destroy = function () {
        this.consume()
    }

    // FrontFrame takes a blob from the BackFrame and loads it into an image.
    // There are two FrontFrames. One is always loading the next image and one
    // is always being used to render the latest fully loaded image. They're
    // swapped once loading completes and the process repeats.
    function FrontFrame(name) {
        this.name = name
        this.blob = null
        this.image = new Image()
        this.url = null
        this.loading = false
        this.loaded = false
        this.fresh = false

        this.onLoad = function () {
            this.loading = false
            this.loaded = true
            this.fresh = true
        }.bind(this)

        this.onError = function () {
            this.loading = false
            this.loaded = false
        }.bind(this)
    }

    FrontFrame.prototype.load = function (blob) {
        // If someone's calling load() they're already sure that they don't need
        // the the current frame anymore.
        this.reset()

        // Convenience check that must come after the reset.
        if (!blob) {
            return
        }

        this.blob = blob
        this.url = URL.createObjectURL(this.blob)
        this.loading = true
        this.loaded = false
        this.fresh = false
        this.image.onload = this.onLoad
        this.image.onerror = this.onError
        this.image.src = this.url
    }

    FrontFrame.prototype.reset = function () {
        this.loading = false
        this.loaded = false
        if (this.blob) {
            this.blob = null
            URL.revokeObjectURL(this.url)
            this.url = null
        }
    }

    FrontFrame.prototype.consume = function () {
        if (!this.fresh) {
            return null
        }

        this.fresh = false
        return this
    }

    FrontFrame.prototype.destroy = function () {
        this.reset()
        this.image = null
    }

    function Pipeline() {
        this.back = new BackFrame()
        this.mid = new FrontFrame('mid')
        this.front = new FrontFrame('front')
    }

    Pipeline.prototype.push = function (blob) {
        this.back.swap(blob)

        // If the mid frame isn't loading or waiting to be consumed, let's
        // ask it to load this new frame to speed up things.
        if (!this.mid.loading && !this.mid.loaded) {
            this.mid.load(this.back.consume())
        }
    }

    Pipeline.prototype.consume = function () {
        if (this.mid.loaded) {
            const mid = this.mid
            this.mid = this.front
            this.front = mid
            this.mid.load(this.back.consume())
        } else if (!this.mid.loading) {
            this.mid.load(this.back.consume())
        }

        return this.front.consume()
    }

    Pipeline.prototype.destroy = function () {
        this.back.destroy()
        this.mid.destroy()
        this.front.destroy()
    }

    // RenderLoop consumes and renders the pipeline.
    function RenderLoop(pipeline, canvas) {
        this.timer = null
        this.pipeline = pipeline
        this.canvas = canvas
        this.g = canvas.getContext('2d')
    }

    RenderLoop.prototype.start = function () {
        this.stop()
        this.next()
    }

    RenderLoop.prototype.stop = function () {
        cancelAnimationFrame(this.timer)
    }

    RenderLoop.prototype.next = function () {
        this.timer = requestAnimationFrame(this.run.bind(this))
    }

    RenderLoop.prototype.run = function () {
        var frame = this.pipeline.consume()
        if (frame) {
            this.canvas.width = frame.image.width
            this.canvas.height = frame.image.height
            this.g.drawImage(frame.image, 0, 0)
        }
        this.next()
    }

    var pipeline = new Pipeline()
    var canvas = document.getElementById('screen')
    var renderLoop = new RenderLoop(pipeline, canvas)

    var ws = new WebSocket(WSURL)
    ws.binaryType = 'blob'

    ws.onclose = function () {
        console.log('onclose', arguments)
        renderLoop.stop()
    }

    ws.onerror = function () {
        console.log('onerror', arguments)
        renderLoop.stop()
    }

    ws.onmessage = function (message) {
//        console.log('got jpeg', arguments)
        var blob = new Blob([message.data], {
            type: 'image/jpeg'
        })
        pipeline.push(blob)
    }

    ws.onopen = function () {
        console.log('onopen', arguments)
        ws.send('1920x1080/0')
        renderLoop.start()
    }

</script>


</html>